// 装饰器是一种特殊类型的声明，它能够被附加到类声明，方法， 访问符，属性或参数上，可以修改类的行为。
// 装饰器使用 @expression这种形式，expression求值后必须为一个函数，它会在运行时被调用，被装饰的声明信息做为参数传入。

// function setProp(target) {

// }

// @setProp

// 装饰器工厂
// function setProp() {
//   return function (target) {
//     //...
//   }
// }

// @setProp()
// 执行顺序 从后往前调用 装饰器工厂最先调用
// 如果是装饰器工厂 先求值再顺序调用 然后得到装饰器 从后往前调用
// @setProp()
// @setName()
// @setAge()

// function setName() {
//   console.log('get setName')
//   return (target: any) => {
//     console.log('setName')
//   }
// }

// function setAge() {
//   console.log('get setAge')
//   return (target: any) => {
//     console.log('setAge')
//   }
// }
// @setName()
// @setAge()
// class ClassDec {
//   constructor() {}
// }

//装饰器求值
/* 
   类的定义中,不同声明上的装饰器有规定的顺序,并进行引用
   参数装饰器、方法装饰器、访问符装饰器、属性装饰器应用到每个实例成员上，
   参数装饰器、方法装饰器、访问符装饰器、属性装饰器应用到每个静态成员上，
   参数装饰器应用到构造函数上
   类装饰器应用到类上

*/
// 类装饰器
//类装饰器在类声明之前声明,装饰器要紧挨着要修饰的内容,类修饰器表达式会在运行的时候当做函数被调用,有一个唯一的参数，就是装饰的这个类
// let sign = null
// function setName(name: string) {
//   return (target: new () => any) => {
//     sign = target
//     console.log(target.name)
//   }
// }
// @setName('胭脂')
// class ClassDec {
//   constructor() {}
// }

// console.log(sign === ClassDec)
// console.log(sign === ClassDec.prototype.constructor)
// function addName(constructor: new () => any) {
//   constructor.prototype.name = 'yazhi'
// }

// @addName
// class ClassD {
//   constructor() {}
// }
// interface ClassD {
//   name: string
// }

// const d = new ClassD()
// console.log(d.name)

// 类装饰器返回一个值，会使用这个值替代被装饰的类的声明,所以可以使用这个特性去修改类的实现,但是需要我们去处理原有的原型链

// function classDecorator<T extends { new (...args: any[]): {} }>(target: T) {
//   return class extends target {
//     newProperty = 'new proerty'
//     hello = 'override'
//   }
// }
// function classDecorator(target: any): any {
//   return class {
//     newProperty = 'new proerty'
//     hello = 'override'
//   }
// }

// @classDecorator
// class Greeter {
//   property = 'property'
//   hello: string
//   constructor(m: string) {
//     this.hello = m
//   }
// }

// console.dir(Greeter)
// console.log(new Greeter('world'))
// 方法装饰器用来处理类中的方法,他可以以处理方法的属性描述符,可以处理方法的定义,方法装饰器在运行的时候也是被当做方法调用
// 包含三个参数 第一个参数:分两种情况(1)如果装饰的是静态成员参数代表是类的构造函数(2)如果装饰的是实例成员,是类的原型对象
// 第二个参数:成员的名字
// 第三个参数:成员的属性描述符

// js属性装饰符
// configurable
// writeble
// enumerable

// interface Iobj {
//   [propName: string]: any
// }

// let obj2: Iobj = {
//   age: 18,
//   sex: '男',
// }
// Object.defineProperty(obj2, 'name', {
//   value: 'yanzhi',
//   writable: false,
//   enumerable: false,
//   configurable: false,
// })
// Object.defineProperty(obj2, 'name', {
//   value: 'yanzhi',
//   writable: true,
// })
// console.log(obj2.name)
// obj2.name = '嘿嘿'
// console.log(obj2.name)

// for (let k in obj2) {
//   console.log(k)
// }

// function enumerable(bool: boolean) {
//   return (
//     target: any,
//     propertyName: string,
//     descriptor: PropertyDescriptor
//   ) => {
//     console.log(target, propertyName, descriptor)
//     descriptor.enumerable = bool
//   }
// }

// 如果方法装饰器返回一个值,那么就会用这个值作为方法的属性描述符对象
// 当构建目标小于es5时 方法装饰器的返回值会被忽略
// function enumerable(bool: boolean): any {
//   return (
//     target: any,
//     propertyName: string,
//     descriptor: PropertyDescriptor
//   ) => ({
//     value() {
//       return 'no age'
//     },
//     enumerable: bool,
//   })
// }

// class ClassF {
//   constructor(public age: number) {}
//   @enumerable(false)
//   public getAge() {
//     return this.age
//   }
// }

// const classF = new ClassF(19)
// console.log(classF.getAge())

// for (const key in classF) {
//   console.log(key)
// }

// 访问器装饰器
// 在get 和 set前面使用一个装饰器就可以了
// function enumerable(bool: boolean) {
//   return (
//     target: any,
//     propertyName: string,
//     descriptor: PropertyDescriptor
//   ) => {
//     descriptor.enumerable = bool
//   }
// }

// class ClassG {
//   private _name: string
//   constructor(name: string) {
//     this._name = name
//   }
//   @enumerable(true)
//   get name() {
//     return this._name
//   }
//   set name(value) {
//     this._name = value
//   }
// }

// const classG = new ClassG('胭脂')

// for (const key in classG) {
//   console.log(key)
// }
// 属性装饰器声明在属性的声明之前，有两个参数:(1)第一个参数是目标(2)第二个参数是名字
// 属性装饰器没法操作属性的属性描述符,他只能用来判断某个类中声明了某个名字的属性

// function printPropertyName(target: any, propertyName: string) {
//   console.log(propertyName)
// }

// class ClassH {
//   @printPropertyName
//   name!: string
// }

// 参数装饰器
// 包含三个参数 第一个参数:分两种情况(1)如果装饰的是静态成员参数代表是类的构造函数(2)如果装饰的是实例成员,是类的原型对象
// 第二个参数:成员的名字
// 第三个参数:参数在函数参数列表中的索引
// 参数装饰器的返回值会被忽略
function required(target: any, propertyName: string, index: number) {
  console.log(`修饰的是${propertyName}的第${index + 1}个参数`)
}

class ClassI {
  name: string = 'lison'
  age: number = 19
  getInfo(prefix: string, @required infoType: string): any {
    return prefix + ' ' + this[infoType]
  }
}

interface ClassI {
  [key: string]: any
}

const classI = new ClassI()
console.log(classI.getInfo('heihei', 'age'))
